﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Windows8Accelerator.Core.Xaml.Controls
{
    public enum ItemLengthUnitTypes
    { 
        Pixel = 1,
        TotalFraction = 2,
        Auto = 3,
        ItemFraction = 4,
    }

    public class ItemLength
    {
        private static CultureInfo USCulture = new CultureInfo("en-US");

        public ItemLength(string text)
        {
            if (text.Contains("/"))
            {
                if (text.ToLower().StartsWith("i"))
                {
                    _UnitType = ItemLengthUnitTypes.ItemFraction;
                    text = text.Substring(1);
                }
                else
                    _UnitType = ItemLengthUnitTypes.TotalFraction;

                double offset = 0;
                if (text.Contains("-"))
                {
                    var offsets = text.Split('-');
                    text = offsets[0];
                    double.TryParse(offsets[1].Trim(), NumberStyles.Any, USCulture, out offset);
                    offset = -offset;
                }
                else if (text.Contains("+"))
                {
                    var offsets = text.Split('+');
                    text = offsets[0];
                    double.TryParse(offsets[1].Trim(), NumberStyles.Any, USCulture, out offset);
                }
                Offset = offset;

                _Value = 0.25;
                var ops = text.Split('/');
                _Numerator = 1;
                _Denominator = 1;
                double.TryParse(ops[0].Trim(), NumberStyles.Any, USCulture, out _Numerator);
                if (ops.Length > 1)
                    double.TryParse(ops[1].Trim(), NumberStyles.Any, USCulture, out _Denominator);
                if (_Denominator != 0)
                    _Value = _Numerator / _Denominator;
            }
            else if (text.ToUpper().Contains("AUTO"))
            {
                _Value = double.NaN;
                _Numerator = 1;
                _Denominator = 0;
                _UnitType = ItemLengthUnitTypes.Auto;
            }
            else
            {
                _Value = 0;
                double.TryParse(text.Trim(), out _Value);
                _Numerator = _Value;
                _Denominator = 1;
                _UnitType = ItemLengthUnitTypes.Pixel;                
            }
        }

        private double _Value;
        public double Value
        {
            get { return _Value; }
            set { _Value = value; }
        }

        private double _Numerator;
        public double Numerator
        {
            get { return _Numerator; }
            set 
            { 
                _Numerator = value;
                _UnitType = ItemLengthUnitTypes.TotalFraction;
                if (_Denominator != 0)
                    _Value = _Numerator / _Denominator;
            }
        }

        private double _Denominator;
        public double Denominator
        {
            get { return _Denominator; }
            set 
            { 
                _Denominator = value;
                _UnitType = ItemLengthUnitTypes.TotalFraction;
                if (_Denominator != 0)
                    _Value = _Numerator / _Denominator;
            }
        }

        public static ItemLength operator *(ItemLength op1, double op2)
        {
            op1.Numerator *= op2;
            return op1;
        }
        public static ItemLength operator *(double op2, ItemLength op1)
        {
            op1.Numerator *= op2;
            return op1;
        }
        public static ItemLength operator /(ItemLength op1, double op2)
        {
            op1.Numerator /= op2;
            return op1;
        }
        public static ItemLength operator /(double op2, ItemLength op1)
        {
            op1.Numerator /= op2;
            return op1;
        }
        public static bool operator ==(ItemLength op1, ItemLength op2)
        {
            return op1.Equals(op2);
        }
        public static bool operator !=(ItemLength op1, ItemLength op2)
        {
            return !op1.Equals(op2);
        }

        public override bool Equals(object obj)
        {
            if (obj is ItemLength)
            {
                var op1 = (ItemLength)obj;
                return
                    this.Denominator.Equals(op1.Denominator) &&
                    this.Numerator.Equals(op1.Numerator) &&
                    this.Value.Equals(op1.Value) &&
                    this.UnitType.Equals(op1.UnitType);
            }
            return base.Equals(obj);
        }

        public static ItemLength Auto
        {
            get
            {
                return new ItemLength("Auto");
            }
        }

        private ItemLengthUnitTypes _UnitType;
        public ItemLengthUnitTypes UnitType
        {
            get { return _UnitType; }
            private set { _UnitType = value; }
        }

        public static explicit operator ItemLength(string text)
        {
            return new ItemLength(text);
        }

        public override string ToString()
        {
            switch (UnitType)
            {
                case ItemLengthUnitTypes.Pixel:
                    return String.Format(USCulture, "{0}",_Value);
                case ItemLengthUnitTypes.TotalFraction:
                case ItemLengthUnitTypes.ItemFraction:
                    var result = String.Format(USCulture, "{0}/{1}", _Numerator, _Denominator);
                    if (Offset > 0)
                        result += String.Format(USCulture, "+{0}", Offset);
                    else if (Offset < 0)
                        result += String.Format(USCulture, "{0}", Offset);
                    if (UnitType == ItemLengthUnitTypes.ItemFraction)
                        result = "i" + result;
                    return result;
                case ItemLengthUnitTypes.Auto:
                    break;
                default:
                    break;
            }
            return base.ToString();
        }

        public override int GetHashCode()
        {
            return
                this.Denominator.GetHashCode() ^
                this.Numerator.GetHashCode() ^
                this.Value.GetHashCode() ^
                this.UnitType.GetHashCode();
        }

        public double ToDouble(double totalLength, double itemLength)
        {
            switch (UnitType)
            {
                case ItemLengthUnitTypes.Pixel:
                    return Value;
                case ItemLengthUnitTypes.TotalFraction:
                    return (totalLength * Value) + Offset;
                case ItemLengthUnitTypes.ItemFraction:
                    return (itemLength * Value) + Offset;
                case ItemLengthUnitTypes.Auto:
                default:
                    return double.NaN;
            }
        }

        public double Offset { get; set; }
    }
}
